/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	ComponentPhysics.cpp
*
*	Comments	-	See ComponentPhysics.h
*
**************************************************************************************/

#include "../Include/ComponentPhysics.h"

#include "../Include/List.h"
#include "../Include/Actor.h"
#include "../Include/Scene.h"
#include "../Include/PhysicsSystem.h"
#include "../Include/PhysicsEvents.h"

#include <algorithm>
#include <list>
#include <set>

// External include
#include "btBulletDynamicsCommon.h"

namespace Pulse
{
	struct ComponentPhysics::InternalData
	{
		typedef std::pair< const ComponentPhysics*, CollisionData> CollisionPair;
		typedef std::list<CollisionPair> CollisionDataList;
		typedef std::set<CollisionPair> CollisionPairSet;

		CollisionDataList m_newCollisionList;
		CollisionPairSet m_collisionPairs;

	};

	bool InternalDataComp(const ComponentPhysics::InternalData::CollisionPair &elem1, 
		const ComponentPhysics::InternalData::CollisionPair &elem2 )
	{
		return elem1.first < elem2.first;
	}

	ComponentPhysics::ComponentPhysics( Actor *pOwner )
		: IComponent( pOwner ), m_pCollider( PSX_NULL ), m_pInternalData( PSX_NULL )
	{
	}

	ComponentPhysics::~ComponentPhysics( void )
	{
		Cleanup();
	}

	EErrorCode::Type ComponentPhysics::Initialize( XMLElementPtr pElem )
	{
		if ( m_pInternalData )
		{
			PSX_PushWarning( "Physics component already initialized." );
			return EErrorCode::OKAY;
		}

		IPhysicsSystem *pPhysSys = GetOwnerUnsafe()->GetScene()->GetPhysicsSystem();
		m_pInternalData = new InternalData;

		// Currently hard-coded to create box collider. Ultimately we'll have more
		//	types of colliders.
		EErrorCode::Type retCode = pPhysSys->ConfigureBoxCollider( this );

		if ( retCode != EErrorCode::OKAY )
		{
			PSX_PushError( "Failed to configure component physics." );
			return retCode;
		}

		return EErrorCode::OKAY;
	}

	void ComponentPhysics::Update( FLOAT dt )
	{
		//// Store new collisions in set first
		ComponentPhysics::InternalData::CollisionPairSet newCollisionSet( m_pInternalData->m_newCollisionList.begin(),
			m_pInternalData->m_newCollisionList.end() );

		// Process Collision End
		{
			ComponentPhysics::InternalData::CollisionPairSet collisionDiff;

			std::set_difference( m_pInternalData->m_collisionPairs.begin(),
				m_pInternalData->m_collisionPairs.end(),
				newCollisionSet.begin(), newCollisionSet.end(),
				std::inserter( collisionDiff, collisionDiff.begin() ) );

			ComponentPhysics::InternalData::CollisionPairSet::iterator iter = collisionDiff.begin();
			ComponentPhysics::InternalData::CollisionPairSet::iterator iterEnd = collisionDiff.end();
		
			while ( iter != iterEnd )
			{
				// Send event for Collision End
				GetOwnerUnsafe()->ExecuteEvent( &EventCollisionExit( iter->second ) );
				++iter;
			}
		}

		// Process Collision Stay
		{
			ComponentPhysics::InternalData::CollisionPairSet output;

			std::set_intersection( newCollisionSet.begin(), newCollisionSet.end(), 
				m_pInternalData->m_collisionPairs.begin(),
				m_pInternalData->m_collisionPairs.end(),
				std::inserter( output, output.begin() ) );

			ComponentPhysics::InternalData::CollisionPairSet::iterator iter = output.begin();
			ComponentPhysics::InternalData::CollisionPairSet::iterator iterEnd = output.end();

			while ( iter != iterEnd )
			{
				// Send event for Collision Stay...
				GetOwnerUnsafe()->ExecuteEvent( &EventCollisionStay( iter->second ) );
				++iter;
			}
		}


		// Process Collision Start
		{
			ComponentPhysics::InternalData::CollisionPairSet collisionDiff;

			std::set_difference( newCollisionSet.begin(), newCollisionSet.end(),
				m_pInternalData->m_collisionPairs.begin(),
				m_pInternalData->m_collisionPairs.end(),
				std::inserter( collisionDiff, collisionDiff.begin() ) );

			ComponentPhysics::InternalData::CollisionPairSet::iterator iter = collisionDiff.begin();
			ComponentPhysics::InternalData::CollisionPairSet::iterator iterEnd = collisionDiff.end();
		
			while ( iter != iterEnd )
			{
				// Send event for Collision Start
				GetOwnerUnsafe()->ExecuteEvent( &EventCollisionEnter( iter->second ) );
				++iter;
			}
		}
	}

	void ComponentPhysics::OnRemoveCollider( void )
	{
		// NOTE: Search through the CollisionData, notify the collider pair and nullify (don't delete)

		return;

		// I'm not really sure about this...
		{
			InternalData::CollisionDataList::iterator iter = m_pInternalData->m_newCollisionList.begin();
			InternalData::CollisionDataList::iterator iterEnd = m_pInternalData->m_newCollisionList.end();
		
			while ( iter != iterEnd )
			{

				//iter->first->GetOwnerUnsafe()->ExecuteEvent( );

				++iter;
			}
		}

	}

	void ComponentPhysics::Cleanup( void )
	{
		PSX_SafeDelete( m_pInternalData );
		GetOwnerUnsafe()->GetScene()->GetPhysicsSystem()->RemoveComponentCollider( this );
	}

	void ComponentPhysics::OnManifold( CollisionData collisionData )
	{
		InternalData::CollisionPair newPair( collisionData.pContactBody, collisionData );
		m_pInternalData->m_newCollisionList.push_back( PSX_Move( newPair ) );
	}

}